
// PhotoEditDlg.cpp : implementation file
//

#include "stdafx.h"
#include "PhotoEdit.h"
#include "PhotoEditDlg.h"
#include "afxdialogex.h"

#include <vector>
#include <string>
using namespace std;

#include <gdiplus.h>
using namespace Gdiplus;

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


CPhotoEditDlg::CPhotoEditDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CPhotoEditDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	/*GdiplusStartupInput gdiplusStartupInput;
	GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);*/
}

void CPhotoEditDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_TREE_DIRECTORY, m_ctrTree);
	DDX_Control(pDX, IDC_STATIC_PICTURE, m_ctrPictureBox);
	DDX_Control(pDX, IDC_TAB_EDIT, m_ctrTab);
	DDX_Control(pDX, IDC_BUTTON_SAVE, m_ctrSave);
	DDX_Control(pDX, IDC_BUTTON_SAVEAS, m_ctrSaveAs);
	DDX_Control(pDX, IDC_STATIC_INFO, m_ctrFileInfo);
	DDX_Control(pDX, IDC_LIST_FILE, m_ctrList);
	DDX_Control(pDX, IDC_CHECK_FITSCREEN, m_ctrCheckFit);
}

BEGIN_MESSAGE_MAP(CPhotoEditDlg, CDialogEx)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_WM_SIZE()
	ON_WM_GETMINMAXINFO()
	ON_COMMAND(IDM_FILE_OPEN, &CPhotoEditDlg::OnFileOpen)
	ON_STN_CLICKED(IDC_STATIC_INFO, &CPhotoEditDlg::OnStnClickedStaticInfo)
	ON_BN_CLICKED(IDC_BUTTON_SAVE, &CPhotoEditDlg::OnBnClickedButtonSave)
	ON_BN_CLICKED(IDC_BUTTON_SAVEAS, &CPhotoEditDlg::OnBnClickedButtonSaveas)
	ON_COMMAND(IDM_FILE_SAVE, &CPhotoEditDlg::OnFileSave)
	ON_COMMAND(IDM_FILE_SAVEAS, &CPhotoEditDlg::OnFileSaveas)
	ON_COMMAND(IDM_EDIT_UNDO, &CPhotoEditDlg::OnEditUndo)
	ON_NOTIFY(TVN_ITEMEXPANDING, IDC_TREE_DIRECTORY, &CPhotoEditDlg::OnTvnItemexpandingTreeDirectory)
	ON_NOTIFY(NM_CLICK, IDC_LIST_FILE, &CPhotoEditDlg::OnNMClickListFile)
	ON_WM_CTLCOLOR()
	ON_WM_CLOSE()

	ON_BN_CLICKED(IDC_CHECK_FITSCREEN, &CPhotoEditDlg::OnBnClickedCheckFitscreen)

	ON_NOTIFY(TVN_ITEMCHANGED, IDC_TREE_DIRECTORY, &CPhotoEditDlg::OnTvnItemChangedTreeDirectory)
END_MESSAGE_MAP()




vector<TCHAR*> CPhotoEditDlg::GetAllSubFolders(TCHAR* lpszFileName)
{
	int l = _tcsnlen(lpszFileName,MAX_PATH);
	TCHAR* findString = new TCHAR[l+3];
	_tcsncpy( findString,lpszFileName,l+1);
	_tcsncpy( findString+l,_T("\\*"),3);
	vector<TCHAR*> listFile;
	WIN32_FIND_DATA findFileData;
	HANDLE hFind = ::FindFirstFile(findString, &findFileData);
	if (hFind != INVALID_HANDLE_VALUE)
	{	// iterate thru directory contents, find subdirectory names
		do
		{	if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			{
				//findFileData.cFileName;
				if(findFileData.cFileName[0] == _T('.'))
				{
					continue;
				}
				int fl = _tcsnlen(findFileData.cFileName,MAX_PATH);
				TCHAR* subfile = new TCHAR[l+fl+2];
				_tcsncpy(subfile,lpszFileName,l);
				subfile[l] = _T('\\');
				_tcsncpy(subfile+l+1,findFileData.cFileName,fl+1);
				listFile.push_back(subfile);
			}
		} while (::FindNextFile(hFind, &findFileData));
		::FindClose(hFind);
	}
	delete []findString;
	return listFile;
}

void CPhotoEditDlg::GetAllFile(TCHAR* lpszFileName,TCHAR* extend,vector<TCHAR*> &listFile)
{
	int l = _tcsnlen(lpszFileName,MAX_PATH);
	int e = _tcsnlen(extend,MAX_PATH);
	TCHAR* findString = new TCHAR[l+e+1];
	_tcsncpy( findString,lpszFileName,l+1);
	_tcsncpy( findString+l,extend,e+1);
	//vector<TCHAR*> listFile;
	WIN32_FIND_DATA findFileData;
	HANDLE hFind = ::FindFirstFile(findString, &findFileData);
	if (hFind != INVALID_HANDLE_VALUE)
	{	// iterate thru directory contents, find subdirectory names
		do
		{	if (!(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
			{
				//findFileData.cFileName;
				if(findFileData.cFileName[0] == _T('.'))
				{
					continue;
				}
				int fl = _tcsnlen(findFileData.cFileName,MAX_PATH);
				TCHAR* subfile = new TCHAR[l+fl+2];
				_tcsncpy(subfile,lpszFileName,l);
				subfile[l] = _T('\\');
				_tcsncpy(subfile+l+1,findFileData.cFileName,fl+1);
				listFile.push_back(subfile);
			}
		} while (::FindNextFile(hFind, &findFileData));
		::FindClose(hFind);
	}
	delete []findString;
}
TCHAR* CPhotoEditDlg::GetName(TCHAR* lpszFileName)
{
	CString str(lpszFileName);
	unsigned found = str.ReverseFind(_T('\\'));
	LPTSTR lpszData = new TCHAR[MAX_PATH];
	_tcscpy(lpszData , str.Mid(found+1));
	return lpszData;
}
HTREEITEM CPhotoEditDlg::InsertDirectory(TCHAR* lpszFileName,HTREEITEM parent,int img)
{
	int m = TVIF_CHILDREN|TVIF_IMAGE|TVIF_PARAM|TVIF_SELECTEDIMAGE|TVIF_TEXT;
	return m_ctrTree.InsertItem(m,
		GetName(lpszFileName),img,img,0,0,(LPARAM)lpszFileName,parent,NULL);
}
void CPhotoEditDlg::RefreshDirectory()
{
	HTREEITEM item = m_ctrTree.GetSelectedItem();
	TVITEMW tItem;
	tItem.mask = TVIF_CHILDREN|TVIF_IMAGE|TVIF_PARAM|TVIF_SELECTEDIMAGE|TVIF_TEXT;
	tItem.hItem = item;
	m_ctrTree.GetItem(&tItem);
	TCHAR* path = (TCHAR*)tItem.lParam;
	BeginWaitCursor();
	vector<TCHAR*> listFile;
	GetAllFile(path,_T("\\*.jpg"),listFile);
	GetAllFile(path,_T("\\*.png"),listFile);
	GetAllFile(path,_T("\\*.bmp"),listFile);
	DrawThumbnails(listFile);
	EndWaitCursor();
	Invalidate(TRUE);
}

// CPhotoEditDlg message handlers

BOOL CPhotoEditDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	// TODO: Add extra initialization here
	m_brush.CreateSolidBrush(RGB(255,230,255));

	THUMBNAIL_WIDTH=32;
	THUMBNAIL_HEIGHT=32;
	m_TreeWidth = 260;
	m_TabHeight = 100;
	m_isFitScreen = false;

	/// ======== Init Tabs
	m_dlgTabHome.Create(IDD_TAB_HOME, &m_ctrTab);
	m_dlgTabHome.m_pPhoto = &m_photo;
	m_dlgTabHome.m_pDlgParent = this;

	TCITEM item1;
	item1.mask    = TCIF_TEXT | TCIF_PARAM;
	item1.lParam  = (LPARAM)& m_dlgTabHome;
	item1.pszText = _T("Home");
	m_ctrTab.InsertItem(0, &item1);

	// Dialog positionieren
	CRect rcItem,rcClient;   
	m_ctrTab.GetItemRect(0, &rcItem);
	m_ctrTab.GetClientRect(&rcClient);

	m_dlgTabHome.MoveWindow(rcItem.left, rcItem.bottom + 1, rcClient.right-5, rcClient.bottom-rcItem.bottom-3);
	m_dlgTabHome.ShowWindow(SW_SHOW);

	ShowWindow(SW_SHOWMAXIMIZED);


	m_ImageListThumb.Create(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, ILC_COLOR24, 0, 1);
	m_ctrList.SetImageList(&m_ImageListThumb, LVSIL_NORMAL);
	/// ========= TREEVIEW init
	imgl.Create(16, 16, ILC_MASK | ILC_COLOR32,0,0);
	COLORREF rgbTransparentColor = RGB(255,255,255);

	CBitmap bmp1;
	bmp1.LoadBitmap(IDB_BITMAP_FOLDER);
	imgl.Add(&bmp1, rgbTransparentColor);
	CBitmap bmp2;
	bmp2.LoadBitmap(IDB_BITMAP_DESKTOP);
	imgl.Add(&bmp2, rgbTransparentColor);
	CBitmap bmp3;
	bmp3.LoadBitmap(IDB_BITMAP_COMPUTER);
	imgl.Add(&bmp3, rgbTransparentColor);

	CBitmap bmp4;
	bmp4.LoadBitmap(IDB_BITMAP_DRIVE);
	imgl.Add(&bmp4, rgbTransparentColor);

	CBitmap bmp5;
	bmp5.LoadBitmap(IDB_BITMAP_DOCUMENT);
	imgl.Add(&bmp5, rgbTransparentColor);

	m_ctrTree.SetImageList(&imgl,0);

	TCHAR* strPath = new TCHAR[ MAX_PATH ];
	// Get the special folder path.
	SHGetSpecialFolderPath(0,strPath,CSIDL_DESKTOPDIRECTORY,FALSE);
	HTREEITEM desktopTVITEM = InsertDirectory(strPath,m_ctrTree.GetRootItem(),1);
	
	HTREEITEM computerTVITEM = InsertDirectory(_T("Computer"),desktopTVITEM,2);

	TCHAR drive[100];
	GetLogicalDriveStrings(MAX_PATH,drive);
	int i=0;
	while(drive[i*4+1] == _T(':'))
	{
		TCHAR* drivePath = new TCHAR[3];
		_tcscpy(drivePath,drive+4*i);
		drivePath[2] = _T('\0');
		InsertDirectory(drivePath,computerTVITEM,3);
		i++;
	}
	m_ctrTree.Expand(computerTVITEM,TVE_EXPAND);
	TCHAR* docPath = new TCHAR[ MAX_PATH ];
	SHGetSpecialFolderPath(0,docPath,CSIDL_MYDOCUMENTS,FALSE);
	InsertDirectory(docPath,desktopTVITEM,4);
	m_ctrTree.Expand(desktopTVITEM,TVE_EXPAND);

	return TRUE;  // return TRUE  unless you set the focus to a control
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CPhotoEditDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		UpdateInfo();
		CDialogEx::OnPaint();
		m_photo.DrawImage(m_ctrPictureBox,m_isFitScreen);
	}
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CPhotoEditDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

void CPhotoEditDlg::OnSize(UINT nType, int cx, int cy)
{
	CDialogEx::OnSize(nType, cx, cy);

	// TODO: Add your message handler code here
	if(!IsWindow(m_ctrPictureBox))
	{
		return;
	}

	int picX = m_TreeWidth+5;
	int picY = 40;
	int picW = cx-picX;
	int picH = cy-m_TabHeight-picY-5;

	m_ctrCheckFit.MoveWindow(picX+picW-20,picY+picH-20,20,20);
	m_ctrPictureBox.MoveWindow(picX,picY,picW,picH);
	m_ctrTab.MoveWindow(picX,cy-m_TabHeight,picW,m_TabHeight);

	int listY = 3*cy/5;
	m_ctrTree.MoveWindow(0,0,m_TreeWidth,listY-5);
	m_ctrList.MoveWindow(0,listY,m_TreeWidth,cy-listY);

	m_ctrSave.MoveWindow(cx-150,5,70,20);
	m_ctrSaveAs.MoveWindow(cx-75,5,70,20);
	m_ctrFileInfo.MoveWindow(picX,5,cx-150-picX,30);
	if(!IsWindow(m_dlgTabHome))
	{
		return;
	}
	CRect rcItem,rcClient;  

	m_ctrTab.GetItemRect(0, &rcItem);
	m_ctrTab.GetClientRect(&rcClient);
	m_dlgTabHome.MoveWindow(rcItem.left, rcItem.bottom + 1, rcClient.right-5, rcClient.bottom-rcItem.bottom-3);
	Invalidate();
}
void CPhotoEditDlg::OnGetMinMaxInfo(MINMAXINFO* lpMMI)
{
	// TODO: Add your message handler code here and/or call default

	lpMMI->ptMinTrackSize.x = 600;
    lpMMI->ptMinTrackSize.y = 400;
	CDialogEx::OnGetMinMaxInfo(lpMMI);
}

void CPhotoEditDlg::UpdateInfo()
{
	CString filename(m_photo.GetFileName());
	CString w,h;
	w.Format(_T("%d"),m_photo.GetWidth());
	h.Format(_T("%d"),m_photo.GetHeight());
	m_ctrFileInfo.SetWindowText(filename+_T("\n(")+
			w+_T(" x ")+h + _T(")"));

	m_ctrSave.EnableWindow(m_photo.IsEdited());
	
}
void CPhotoEditDlg::OnFileOpen()
{
	// TODO: Add your command handler code here
	CFileDialog fileDlg(TRUE,NULL,NULL,OFN_EXTENSIONDIFFERENT, 
		_T("Image (*.jpeg, *.jpg, *.png, *bmp) |*.jpeg; *.jpg; *.png; *.bmp|"),this);


	if(fileDlg.DoModal() == IDOK)
	{
		if(m_photo.IsEdited())
		{
			if (MessageBox(_T("Do you want to save?"),
				_T("Save?"), MB_YESNO) == IDYES)
			{
				m_photo.Save();
				RefreshDirectory();
			}
		}
		UpdateData(FALSE);
		USES_CONVERSION;
		m_fileName = fileDlg.GetFileName();
		char * p = W2A(fileDlg.GetPathName());
		m_photo.Open(p);
		
		m_ctrSaveAs.EnableWindow();
		m_ctrTab.EnableWindow();
		//m_isFitScreen = false;
		Invalidate(TRUE);
	}
}

void CPhotoEditDlg::OnStnClickedStaticInfo()
{
	// TODO: Add your control notification handler code here
}
void CPhotoEditDlg::OnBnClickedButtonSave()
{
	// TODO: Add your control notification handler code here
	OnFileSave();

}
void CPhotoEditDlg::OnBnClickedButtonSaveas()
{
	// TODO: Add your control notification handler code here
	OnFileSaveas();
}

void CPhotoEditDlg::OnFileSave()
{
	// TODO: Add your command handler code here
	m_photo.Save();
	RefreshDirectory();
}
void CPhotoEditDlg::OnFileSaveas()
{
	// TODO: Add your command handler code here
	CFileDialog fileDlg(FALSE,_T("*.jpg|*.png|*.bmp"),m_fileName,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, 
		_T("JPEG (*.jpg)|*.jpg|PNG (*.png)|*.png|BITMAP (*.bmp)|*.bmp|"),this);
	if(fileDlg.DoModal() == IDOK)
	{
		UpdateData(FALSE);
		USES_CONVERSION;
		m_fileName = fileDlg.GetFileName();
		char * p = W2A(fileDlg.GetPathName());
		m_photo.Save(p);
		RefreshDirectory();
		
	}
}
void CPhotoEditDlg::OnEditUndo()
{
	// TODO: Add your command handler code here
	m_photo.Undo();
	Invalidate(TRUE);
}

void CPhotoEditDlg::OnTvnItemexpandingTreeDirectory(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);

	// TODO: Add your control notification handler code here
	//m_ctrTree.DeleteItem(
	//if(pNMTreeView->itemNew.hItem)

	if(pNMTreeView->itemNew.state&TVIS_EXPANDED)
	{
		return;
	}
	HTREEITEM child = m_ctrTree.GetChildItem(pNMTreeView->itemNew.hItem);
	CString itemName = m_ctrTree.GetItemText(pNMTreeView->itemNew.hItem);
	if(_tcscmp(itemName,_T("Desktop")) == 0)
	{
		child = m_ctrTree.GetNextSiblingItem(child);
		child = m_ctrTree.GetNextSiblingItem(child);
	}else if(_tcscmp(itemName,_T("Computer")) == 0)
	{
		return;
	}
	while(child != NULL)
	{
		HTREEITEM nextChild = m_ctrTree.GetNextSiblingItem(child);
		m_ctrTree.DeleteItem(child);
		child = nextChild;
	}
	TCHAR* path = (TCHAR*)pNMTreeView->itemNew.lParam;
	vector<TCHAR*> subfiles = GetAllSubFolders(path);
	for(unsigned int i=0;i<subfiles.size();i++)
	{
		InsertDirectory(subfiles[i],pNMTreeView->itemNew.hItem,0);
	}
	*pResult = 0;
}

void CPhotoEditDlg::OnTvnItemChangedTreeDirectory(NMHDR *pNMHDR, LRESULT *pResult)
{
	
	NMTVITEMCHANGE *pNMTVItemChange = reinterpret_cast<NMTVITEMCHANGE*>(pNMHDR);
	// TODO: Add your control notification handler code here
	if(pNMTVItemChange->uStateNew&TVIS_SELECTED)
	{
			TCHAR* path = (TCHAR*)pNMTVItemChange->lParam;
		BeginWaitCursor();
		vector<TCHAR*> listFile;
		GetAllFile(path,_T("\\*.jpg"),listFile);
		GetAllFile(path,_T("\\*.png"),listFile);
		GetAllFile(path,_T("\\*.bmp"),listFile);
		
		DrawThumbnails(listFile);
		EndWaitCursor();
	}

	*pResult = 0;
}

void  CPhotoEditDlg::DrawThumbnails(vector<TCHAR*> files)
{
	CBitmap*    pImage = NULL;
	HBITMAP		hBmp = NULL;
	CString		strPath;
	int			i;

	// hold the window update to avoid flicking
	m_ctrList.SetRedraw(FALSE);

	// reset our image list
	/*
	for (i = 0; i<m_ImageListThumb.GetImageCount(); i++)
		m_ImageListThumb.Remove(i);
	*/
	// remove all items from list view
	if (m_ctrList.GetItemCount() != 0)
		m_ctrList.DeleteAllItems();

	// set the size of the image list
	m_ImageListThumb.SetImageCount(files.size());
	i = 0;

	// draw the thumbnails
	std::vector<TCHAR*>::iterator	iter;
	for (iter = files.begin(); iter != files.end(); iter++)
	{
		//USES_CONVERSION;
		Bitmap img( *iter );
		Bitmap* pThumbnail = static_cast<Bitmap*>(img.GetThumbnailImage(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, NULL, NULL));

		// attach the thumbnail bitmap handle to an CBitmap object
		pThumbnail->GetHBITMAP(NULL, &hBmp);
		pImage = new CBitmap();		 
		pImage->Attach(hBmp);
		// add bitmap to our image list
		m_ImageListThumb.Replace(i, pImage, NULL);
		//m_ImageListThumb.Add(pImage,RGB(255,255,255));
		// put item to display
		m_ctrList.InsertItem(LVIF_PARAM|LVIF_TEXT|LVIF_IMAGE,i,GetName(files[i]),
			i,i,i,(LPARAM)files[i]);
		i++;
		delete pImage;
		delete pThumbnail;
	}
	// let's show the new thumbnails
	m_ctrList.SetRedraw(); 
}

void CPhotoEditDlg::OnNMClickListFile(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
	// TODO: Add your control notification handler code here
	if(pNMItemActivate->iItem == -1)
	{
		return;
	}
	UpdateData(FALSE);
	USES_CONVERSION;
	
	LVITEMW item;
	item.iItem = pNMItemActivate->iItem;
	item.iSubItem = pNMItemActivate->iSubItem;
	m_ctrList.GetItem(&item);
	CString text((TCHAR*)item.lParam) ;
	
	if(m_fileName == text)
	{
		return;
	}else if(m_photo.IsEdited())
	{
		if (MessageBox(_T("Do you want to save?"),
			_T("Save?"), MB_YESNO) == IDYES)
		{
			m_photo.Save();
			RefreshDirectory();
		}
	}

	m_fileName = text;
	//
	char * p = W2A(text);
	m_photo.Open(p);
	m_ctrSaveAs.EnableWindow();
	m_ctrTab.EnableWindow();
	Invalidate(TRUE);
	//m_isFitScreen = false;
	*pResult = 0;
}



void CPhotoEditDlg::OnClose()
{
	// TODO: Add your message handler code here and/or call default
	if(m_photo.IsEdited())
	{
		int r = MessageBox(_T("Do you want to save?"),
			_T("Save?"), MB_YESNOCANCEL);
		if (r == IDYES)
		{
			m_photo.Save();
			RefreshDirectory();
		}
		else if(r == IDCANCEL)
		{
			return;
		}
	}
	CDialogEx::OnClose();
}

void CPhotoEditDlg::OnBnClickedCheckFitscreen()
{
	// TODO: Add your control notification handler code here
	m_isFitScreen = m_ctrCheckFit.GetCheck();
	Invalidate();
}